/* 
 * Copyright (c) Sergey B. Berezin (s_berezin@cs.msu.su), 2002, 2003.
 *
 * You may freely distribute or modify this source code retaining the 
 * above copyright notice.
 *
 * This software is provided "as-is" and without guarantee or warrantee 
 * of any kind, expressed or implied :)
 */

using OpenGL;
using ManagedGL;
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Diagnostics;
using System.ComponentModel;
using UVS.Base;

namespace UVS.UI {

	/// <summary>
	/// IViewport and derived interfaces provide information
	/// about object being viewed (mostly about coordinate transformation)
	/// </summary>
	public interface IViewport {
		/// <summary>Returns bounding box of viewed object</summary>
		BoxD ObjectBox { get; }
	}

	/// <summary>
	/// IViewportLayer provides information about associated viewport. If layer 
	/// implements this interface all upper layers have access to object returned
	/// by Viewport property until next layer implementing IViewportLayer.
	/// </summary>
	public interface IViewportLayer {
		/// <summary>Return viewport object</summary>
		IViewport Viewport { get; }
	}

	[Flags]
	public enum LayeredViewState : uint {
		UpdateEnabled   = 0x01, // OnRender and OnResize methods for all layers can be called immediately
		UpdateRequested = 0x02, 
		ResizeRequested = 0x04
	}

	public class LayerCollection : ReadOnlySortedCollectionBase {

		public delegate void LayerAddedHandler(LayerCollection sender,Layer layer);
		public delegate void LayerRemovedHandler(LayerCollection sender,Layer layer);

		public event LayerAddedHandler LayerAdded;
		public event LayerRemovedHandler LayerRemoved;

		public void Add(Layer layer) {
			base.Add(layer);
			if(LayerAdded != null)
				LayerAdded(this,layer);
		}

		public void Remove(Layer layer) {
			base.Remove(layer);
			if(LayerRemoved != null)
				LayerRemoved(this,layer);
		}
	}

	/// <summary>
	/// LayeredView is represented by an ordered collection of layers
	/// </summary>
	public class LayeredView : UIView {

		protected LayerCollection layers = new LayerCollection();
		protected LayeredViewState state = LayeredViewState.UpdateEnabled;
		protected IViewport viewport = null;

		public LayeredView() {
			layers.LayerAdded += new LayerCollection.LayerAddedHandler(this.OnLayerAdded);
			layers.LayerRemoved += new LayerCollection.LayerRemovedHandler(this.OnLayerRemoved);
		}

		public bool UpdateEnabled {
			get {
				return ((state & LayeredViewState.UpdateEnabled) != 0);
			}
			set {
				if(value)
					state |= LayeredViewState.UpdateEnabled;
				else
					state &= ~LayeredViewState.UpdateEnabled;
				if(ContextAttached && UpdateEnabled) {
					if((state & LayeredViewState.ResizeRequested) != 0) 
						ResizeAllLayers();
					if((state & LayeredViewState.UpdateRequested) != 0)
						RenderAllLayers();
				}
			}
		}

		public override void OnContextAttached(GLContext ctx) {
			base.OnContextAttached(ctx);
			foreach(Layer l in layers)
				l.OnContextAttached();
		}

		public override void OnContextDetached() {
			foreach(Layer l in layers)
				l.OnContextDetached();
			base.OnContextDetached();
		}

		public override void OnControlAttached(Control c) {
			base.OnControlAttached(c);
			foreach(Layer l in layers) {
				UILayer u = l as UILayer;
				if(u != null)
					u.OnControlAttached();
			}
		}

		public LayerCollection Layers {
			get {
				return layers;
			}
		}

		public override void OnControlDetached() {
			foreach(Layer l in layers) {
				UILayer u = l as UILayer;
				if(u != null)
					u.OnControlDetached();
			}
			base.OnControlDetached();
		}

		public override void OnRender() {
			base.OnRender();
			if(UpdateEnabled)
				RenderAllLayers();
			else
				state |= LayeredViewState.UpdateRequested;
		}

		public override void OnResize(Rectangle vp) {
			base.OnResize(vp);
			if(UpdateEnabled)
				ResizeAllLayers();
			else
				state |= LayeredViewState.ResizeRequested;
		}

		public override void UpdateContextMenu(ContextMenu menu) {
			foreach(Layer l in layers) {
				UILayer u = l as UILayer;
				if(u != null)
					u.UpdateContextMenu(menu);
			}
		}

		public void UpdateAllLayers() {
			if(ContextAttached && UpdateEnabled) {
				context.MakeCurrent();
				RenderAllLayers();
				if(context.IsDoubleBuffered)
					context.SwapBuffers();
			} else
				state |= LayeredViewState.UpdateRequested;
		}

		/// <summary>
		/// Returns current viewport or null if no layers implementing viewports		
		/// were rendered
		/// </summary>
		public IViewport Viewport {
			get {
				return viewport;
			}
		}

		public void ResizeAllLayers() {
			foreach(Layer l in layers)
				l.OnResize();
			state &= ~LayeredViewState.ResizeRequested;
		}

		/// <summary>
		/// Repaints all views and clears UpdateRequested flag. Rendering
		/// context should be attached and made current.
		/// </summary>
		protected void RenderAllLayers() {
			Debug.Assert(context != null);
			GL.glViewport(Rectangle);
			foreach(Layer l in layers) {
				if(l.Visible)
					l.OnRender();
				IViewportLayer iv = l as IViewportLayer;
				if(iv != null) 
					viewport = iv.Viewport;
			}
			state &= ~LayeredViewState.UpdateRequested;
		}	

		public virtual void OnLayerAdded(LayerCollection sender,Layer layer) {
			layer.view = this;
			if(context != null) {
				context.MakeCurrent();
				layer.OnContextAttached();
			}
			if(control != null) {
				UILayer u = layer as UILayer;
				if(u != null)
					u.OnControlAttached();
			}
			UpdateAllLayers();
		}

		public virtual void OnLayerRemoved(LayerCollection sender,Layer layer) {
			if(control != null) {
				UILayer u = layer as UILayer;
				if(u != null) 
					u.OnControlDetached();
			}
			if(context != null) {
				context.MakeCurrent();
				layer.OnContextDetached();
			}
			layer.view = null;
			UpdateAllLayers();
		}

		public bool UpdateRequested {
			get {
				return (state & LayeredViewState.UpdateRequested) != 0;
			}
			set {
				if(value) {
					state |= LayeredViewState.UpdateRequested;
					if(UpdateEnabled && control != null)
						control.Invalidate();
				} else
					state &= ~LayeredViewState.UpdateRequested;
			}
		}
	}
}